﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Core
{
    public class AwesomoControllerClient : AoSocket
    {
        public enum ControllerEventPackets : byte
        {
            CONNECTION_SUCCESSFUL = 0x01,
            ALREADY_CONNECTED = 0x02,
            BNET_AUTH_RESPONSE = 0x03,
            BNET_LOGON_RESPONSE = 0x04,
            TEMPORARY_IP_BAN = 0x05,
            CHARACTER_SELECT = 0x06,
            JOIN_CHANNEL = 0x07,
            LOGON_SUCCESS = 0x08,
            JOIN_GAME_SUCCESSFUL = 0x09,
            CONNECTED_GAME_PROXY = 0x0a,
            GAME_ALREADY_EXISTS = 0x0b,
            GAME_DOES_NOT_EXISTS = 0x0c,
            FAILED_TO_JOIN = 0x0d,
            MESSAGE = 0x0e,
            EXIT_GAME = 0x0f,
            CONTROLLER_MESSAGE = 0x10,
            VARIABLE = 0x11,
            CHAT_EVENT = 0x12,
            CHAT_DESTRUCTOR = 0x13,
            REALM_DESTRUCTOR = 0x14,
            GAME_DESTRUCTOR = 0x15,
            CANT_CONNECT = 0x16,
            CHAR_COUNT = 0x17,

            //script packets
            ITEM_MESSAGE = 0x64,
            STATUS_MESSAGE= 0x65,
            CHICKEN_EXIT = 0x66,
            DIA_CLONE = 0x67,
            HOT_IP = 0x68,
            BEGIN_MULE = 0x69,
            MULEISFULL = 0x70,
            FINISHED_MULE = 0x71,
            JOIN_MULE_GAME = 0x73,
            DEATH = 0x74,
            GAME_EXP = 0x75,

            //AoSocket.cs socket error
            SOCKET_ERROR = 200
        }

        public event AwesomoControllerClient_Handler PacketReceived;

        public AwesomoControllerClient() 
            : base()            
        {
        }

        public AwesomoControllerClient(string ip)
            : base(ip)
        {
        }
 
        public int CreateNewControllerClient(string ownername)
        {
            if (this.Connect())
            {
                List<byte> buffer = new List<byte>();
                buffer.Add(0x01); // tell AO to create a new controller client
                buffer.AddRange(Encoding.ASCII.GetBytes(ownername));
                buffer.Add((byte)'\0');// end the string
                return this.Send(buffer.ToArray(), buffer.Count);
            }
            return 0;
        }

        public bool Start()
        {
            if (IsClientConnected)
                return this.StartListener();
            return false;
        }

        public bool Stop()
        {
            return this.Disconnect();
        }

        public int StartRun()
        {
            List<byte> buffer = new List<byte>();
            buffer.Add(0x02);
            return this.Send(buffer.ToArray(), buffer.Count);
        }

        public int StartMule()
        {
            byte[] buffer = new byte[2];
            buffer[0] = 0x10;
            buffer[1] = (byte)'\0';
            int length = buffer.Length;
            return this.Send(buffer, length);
        }

        public int StartMakeMule()
        {
            byte[] buffer = new byte[2];
            buffer[0] = 0x11;
            buffer[1] = (byte)'\0';
            int length = buffer.Length;
            return this.Send(buffer, length);
        }

        public int StartLeech()
        {
            byte[] buffer = new byte[2];
            buffer[0] = 0x13;
            buffer[1] = (byte)'\0';
            int length = buffer.Length;
            return this.Send(buffer, length);
        }

        public int SendMsgToConClients(string msg)
        {
            List<byte> buffer = new List<byte>();
            buffer.Add(0x03);
            buffer.AddRange(Encoding.ASCII.GetBytes(msg));
            buffer.Add((byte)'\0');
            return this.Send(buffer.ToArray(), buffer.Count);
        }

        public int SetCoreVar(string varname, string value)
        {
            List<byte> buffer = new List<byte>();
            buffer.Add(0x04);
            buffer.AddRange(Encoding.ASCII.GetBytes(varname));
            buffer.Add((byte)'\0');
            buffer.AddRange(Encoding.ASCII.GetBytes(value));
            buffer.Add((byte)'\0');
            return this.Send(buffer.ToArray(), buffer.Count);
        }

        public int RunLuaFile(string filename, string function)
        {
            List<byte> buffer = new List<byte>();
            buffer.Add(0x05);
            buffer.AddRange(Encoding.ASCII.GetBytes(filename));
            buffer.Add((byte)'\0');
            buffer.AddRange(Encoding.ASCII.GetBytes(function));
            buffer.Add((byte)'\0');
            return this.Send(buffer.ToArray(), buffer.Count);
        }

        public int LeaveGame()
        {
            List<byte> buffer = new List<byte>();
            buffer.Add(0x06);
            buffer.Add((byte)'\0');
            return this.Send(buffer.ToArray(), buffer.Count);
        }

        public int AddCdkey(string key, string gamever)
        {
            List<byte> buffer = new List<byte>();
            buffer.Add(0x08);
            buffer.AddRange(Encoding.ASCII.GetBytes(key));
            buffer.Add((byte)'\0');
            buffer.AddRange(Encoding.ASCII.GetBytes(gamever));
            buffer.Add((byte)'\0');
            return this.Send(buffer.ToArray(), buffer.Count);
        }

        public int ClearCdkeys()
        {
            byte[] buffer = new byte[]{0x09}; 
            return this.Send(buffer, buffer.Length);
        }

        public int ReleaseCdkeys()
        {
            byte[] buffer = new byte[] { 0x12 };
            return this.Send(buffer, buffer.Length);
        }

        protected override void PacketHandler(byte[] packets)
        {
            Console.WriteLine(((ControllerEventPackets)packets[0]).ToString());

            switch ((ControllerEventPackets)packets[0])
            {        
                case ControllerEventPackets.CONNECTION_SUCCESSFUL:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CONNECTION_SUCCESSFUL));
                    break;

                case ControllerEventPackets.ALREADY_CONNECTED:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ALREADY_CONNECTED));
                    break;

                case ControllerEventPackets.BNET_AUTH_RESPONSE:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.BNET_AUTH_RESPONSE, (int)packets[1]));
                    break;

                case ControllerEventPackets.BNET_LOGON_RESPONSE:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.BNET_LOGON_RESPONSE, (int)packets[1]));
                    break;

                case ControllerEventPackets.TEMPORARY_IP_BAN:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.TEMPORARY_IP_BAN));
                    break;

                case ControllerEventPackets.CHARACTER_SELECT:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CHARACTER_SELECT));
                    break;

                case ControllerEventPackets.JOIN_CHANNEL:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.JOIN_CHANNEL));
                    break;

                case ControllerEventPackets.LOGON_SUCCESS:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.LOGON_SUCCESS));
                    break;

                case ControllerEventPackets.JOIN_GAME_SUCCESSFUL:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.JOIN_GAME_SUCCESSFUL));
                    break;

                case ControllerEventPackets.CONNECTED_GAME_PROXY:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CONNECTED_GAME_PROXY));
                    break;

                case ControllerEventPackets.GAME_ALREADY_EXISTS:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.GAME_ALREADY_EXISTS));
                    break;

                case ControllerEventPackets.GAME_DOES_NOT_EXISTS:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.GAME_DOES_NOT_EXISTS));
                    break;

                case ControllerEventPackets.FAILED_TO_JOIN:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.FAILED_TO_JOIN));
                    break;

                case ControllerEventPackets.MESSAGE:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.MESSAGE, BitConverter.ToString(packets, 1)));
                    break;

                case ControllerEventPackets.EXIT_GAME:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.EXIT_GAME));
                    break;

                case ControllerEventPackets.CONTROLLER_MESSAGE:
                    break;

                case ControllerEventPackets.VARIABLE:
                    break;

                case ControllerEventPackets.CHAT_EVENT:
                    ChatMessage cmsg = ChatMessageParser.ParseChatMessage(packets);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CHAT_EVENT, cmsg.Account, cmsg.CharName, cmsg.Message));
                    break;

                case ControllerEventPackets.CANT_CONNECT:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CANT_CONNECT));
                    break;

                case ControllerEventPackets.ITEM_MESSAGE:
                    string msg = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.ITEM_MESSAGE, msg));
                    break;

                case ControllerEventPackets.STATUS_MESSAGE:
                    string status = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.STATUS_MESSAGE, status));
                    break;

                case ControllerEventPackets.CHICKEN_EXIT:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CHICKEN_EXIT));
                    break;

                case ControllerEventPackets.DIA_CLONE:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.DIA_CLONE,Encoding.ASCII.GetString(packets, 1, packets.Length - 1)));
                    break;

                case ControllerEventPackets.HOT_IP:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.HOT_IP, Encoding.ASCII.GetString(packets, 1, packets.Length - 1)));
                    break;

                case ControllerEventPackets.BEGIN_MULE:
                    string beginMule = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.BEGIN_MULE, beginMule));
                    break;

                case ControllerEventPackets.MULEISFULL:
                    string muleIsFull = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.MULEISFULL, muleIsFull));
                    break;

                case ControllerEventPackets.FINISHED_MULE:
                    string finishedMule = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.FINISHED_MULE, finishedMule));
                    break;

                case ControllerEventPackets.JOIN_MULE_GAME:
                    string joinMule = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.JOIN_MULE_GAME, joinMule));
                    break;

                case ControllerEventPackets.DEATH:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.DEATH));
                    break;

                case ControllerEventPackets.CHAR_COUNT:
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.CHAR_COUNT, (int)packets[1]));
                    break;

                case ControllerEventPackets.GAME_EXP:
                    string gameExperience = Encoding.ASCII.GetString(packets, 1, packets.Length - 1);
                    PacketReceived(this, new PacketArgs(ControllerEventPackets.GAME_EXP, gameExperience));
                    break;
            }
        }

        protected override void SocketErrorHandler(string errmsg)
        {
            PacketReceived(this, new PacketArgs(ControllerEventPackets.SOCKET_ERROR, errmsg));
        }
    }

    #region Controller event

    public delegate void AwesomoControllerClient_Handler(object sender, PacketArgs myArgs);

    public class PacketArgs : EventArgs
    {
        AwesomoControllerClient.ControllerEventPackets _event;
        string _account;
        string _charName;
        string _message;
        int _result;

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets _event) 
            : this(_event, "", 0)
        {
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets _event, string _message)
            : this(_event, _message, 0)
        {
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets _event, int _result)
            : this(_event, "", _result)
        {
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets _event, string _message, int _result)
        {
            this._event = _event;
            this._message = _message;
            this._result = _result;
        }

        public PacketArgs(AwesomoControllerClient.ControllerEventPackets _event, string _account, string _charName, string _message)
        {
            this._event = _event;
            this._account = _account;
            this._charName = _charName;
            this._message = _message;
        }

        public AwesomoControllerClient.ControllerEventPackets Event
        {
            get { return _event; }
        }

        public string Account
        {
            get { return _account; }
        }

        public string CharName
        {
            get { return _charName; }
        }

        public string Message
        {
            get { return _message; }
        }

        public int Result
        {
            get { return _result; }
        }
    }

    #endregion

    public static class ChatMessageParser
    {
        public static ChatMessage ParseChatMessage(byte[] buffer)
        {
            int offset = 1;
            int length = 0;
            
            while (buffer[offset] != 0)
            {
                offset++;
            }

            length = offset - 1;

            string acc = Encoding.ASCII.GetString(buffer, 1, length);
            length = 0;
            offset++;

            while (buffer[offset] != 0)
            {
                offset++;
                length++;
            }

            string charname = Encoding.ASCII.GetString(buffer, acc.Length + 2 ,length);
            length = 0;
            offset++;

            while (buffer[offset] != 0)
            {
                offset++;
                length++;
            }

            string message = Encoding.ASCII.GetString(buffer, acc.Length + charname.Length + 3,length);

            return new ChatMessage(acc, charname, message);
        }
    }

    public class ChatMessage
    {
        public readonly string Account;
        public readonly string CharName;
        public readonly string Message;

        public ChatMessage(string _a, string _c, string _m)
        {
            this.Account = _a;
            this.CharName = _c;
            this.Message = _m;
        }
    }
}
